package demo.dcs;

import demo.dcs.aspect.GenericObjectPool2Listener;
import demo.dcs.aspect.GenericObjectPoolListener;
import demo.dcs.aspect.TomcatPoolAspectJ;
import demo.java.lang.management.jvm.AppTraceConfig;
import demo.java.lang.management.jvm.JVMDumpTask;
import demo.java.lang.management.jvm.LocalAsySendImpl2;
import demo.java.lang.management.vo.MetricSpan;
import demo.java.util.ConcurrentHashSet;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * @author mandy.hu
 * 
 */
public class MertricSendSchedule {

	private static Logger log = LoggerFactory.getLogger(MertricSendSchedule.class);
	private Set<MertricListener> mertricListeners ;
	private static final int defaultSendSpanTime = 60000;
	private LocalAsySendImpl2 asySendService = LocalAsySendHolder.getInstance2();
	
	private static MertricSendSchedule mertricSendSchedule = new MertricSendSchedule();

    private static TomcatPoolAspectJ tomcatPoolAspectJ = new TomcatPoolAspectJ();

	private TransferTask task;

    private JVMDumpTask jvmDumpTask;
	
	public MertricSendSchedule() {
		this.task = new TransferTask();
		this.task.setDaemon(true);
		this.start();

        this.jvmDumpTask = new JVMDumpTask();
        this.jvmDumpTask.setDaemon(true);
        this.jvmStart();
	}

    public void jvmStart() {

        if (!jvmDumpTask.isAlive()) {
            jvmDumpTask.setName("write jvm dump -" + jvmDumpTask.getName());
            jvmDumpTask.setDaemon(true);
            jvmDumpTask.start();
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    jvmDumpTask.interrupt();
                }
            });
        }
    }

    public void start() {
		
		if (!task.isAlive()) {
			task.setName("write metric log-" + task.getName());
			task.setDaemon(true);
			task.start();
			Runtime.getRuntime().addShutdownHook(new Thread() {
				public void run() {
					task.interrupt();
				}
			});
		}
	}
	
	private class TransferTask extends Thread{
		TransferTask() {
			this.setName("TransferTask4Metric-agent-Thread");
		}

		@Override
		public void run() {
			if (mertricListeners == null) {
				return;
			}
			for (;;) {
				try {
					List<MetricSpan> metricSpans = new ArrayList<MetricSpan>();
                    Iterator<MertricListener> iterator = mertricListeners.iterator();
                    while (iterator.hasNext()){
                        MertricListener mertricListener = iterator.next();
                        try {
                            List<MetricSpan> metricSpan1s = mertricListener.collectMetric();
                            if (metricSpan1s!=null && metricSpan1s.size()>0){
                                metricSpans.addAll(metricSpan1s);
                            }
                        } catch (Throwable e) {
                            log.error("MetricSendSchedule TransferTask add metric", e);
                        }
                    }
					if (metricSpans.size() > 0) {
						sendData(metricSpans);
					}
				} catch (Throwable e) {
                    log.error("MetricSendSchedule TransferTask other error", e);
                }

                try {
                    Thread.sleep(getSleepTime());
                } catch (InterruptedException e) {
                    log.error(e.getMessage(), e);
                }
            }
		}
	}


	public int getSleepTime() {

		if (StringUtils.isNotBlank(AppTraceConfig.getLocalConfig().getMetricSpanTime())) {
			return Integer.valueOf(AppTraceConfig.getLocalConfig().getMetricSpanTime());
		}
		return defaultSendSpanTime;

	}

	/**
	 * @param metricSpans
	 */
	private void sendData(List<MetricSpan> metricSpans) {

		asySendService.asyMetricSends(metricSpans);
	}

	
	/**
	 * @return the mertricListeners
	 */
	public Set<MertricListener> getMertricListeners() {
	
		return mertricListeners;
	}
	
	/**
	 * @param mertricListeners the mertricListeners to set
	 */
	public void setMertricListeners(Set<MertricListener> mertricListeners) {
	
		this.mertricListeners = mertricListeners;
	}
	/**
	 * @param mertricListener
	 *  the mertricListeners to set
	 */
	public synchronized void addMertricListener(MertricListener mertricListener) {
        try {
            if (this.mertricListeners==null) {
                this.mertricListeners = new ConcurrentHashSet<MertricListener>();
            }
            boolean isRedisListener = false;
            if (mertricListener instanceof GenericObjectPool2Listener || mertricListener instanceof GenericObjectPoolListener){
                isRedisListener = true;
            }
            boolean needAdd = true;
            if (this.mertricListeners.size()>0){
                Iterator<MertricListener> iterator = this.mertricListeners.iterator();
                while (iterator.hasNext()){
                    MertricListener listener = iterator.next();
                    if (listener.isDestroyed()) {
                    	iterator.remove();
					}else {
						if (listener.isSame(mertricListener)) {
							needAdd=false;
							break;
						}else if (listener.isSameSource(mertricListener)) {
                            if (isRedisListener){//不同业务的redis连接池用同了一个dbindex，用序号标识
                                setRedisPoolIndex(mertricListener, listener);
                            }else{
//								log.warn(AppTraceConfig.getLocalConfig().getAppName() + " has duplicate "+listener.getMertricListenerType()
//										+" source,please send xdcs team");
							}
						}
					}
                    
                }
            }
            if (needAdd) {
            	this.mertricListeners.add(mertricListener);
			}
        }catch (Exception e){
            log.error("MetricSendSchedule addMertricListener error", e);
        }
	}

    private void setRedisPoolIndex(MertricListener newListener, MertricListener oldListener) {
        if (newListener instanceof GenericObjectPool2Listener){
            int newIndex = ((GenericObjectPool2Listener)newListener).getIndex();
            int oldIndex = ((GenericObjectPool2Listener)oldListener).getIndex();
            if (newIndex<=oldIndex){
                newIndex=oldIndex+1;
            }
            ((GenericObjectPool2Listener)newListener).setIndex(newIndex);
        }

        if (newListener instanceof GenericObjectPoolListener){
            int newIndex = ((GenericObjectPoolListener)newListener).getIndex();
            int oldIndex = ((GenericObjectPoolListener)oldListener).getIndex();
            if (newIndex<=oldIndex){
                newIndex=oldIndex+1;
            }
            ((GenericObjectPoolListener)newListener).setIndex(newIndex);
        }
    }

    public synchronized void deleteMertricListener(MertricListener mertricListener) {

		try {
			if (this.mertricListeners != null) {
				this.mertricListeners.remove(mertricListener);
			}
		} catch (Exception e) {
			log.error("MetricSendSchedule deleteMertricListener error", e);
		}
	}

    /**
	 * @return the mertricSendSchedule
	 */
	public static MertricSendSchedule getMertricSendSchedule() {
	
		return mertricSendSchedule;
	}
	
	/**
	 * @param mertricSendSchedule the mertricSendSchedule to set
	 */
	public static void setMertricSendSchedule(MertricSendSchedule mertricSendSchedule) {
	
		MertricSendSchedule.mertricSendSchedule = mertricSendSchedule;
	}
	

}
